home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / Hsoi's App Shell Source / HASMenuWindows.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-28  |  40.2 KB  |  1,253 lines  |  [TEXT/CWIE]

  1. /*
  2.     HASMenuWindows.c from Hsoi's App Shell.  © 1995-1997 John C. Daub.  All rights reserved.
  3.  
  4.     This file contains things pertaining to our "Windows" menu, such as stacking, tiling,
  5.     and things to maintain a running list of our open windows.  The code to maintain
  6.     the menu itself (enable/disable, etc) is in the regular menus file.
  7.     
  8.     This code is based upon code from Scott Knaster and Keith Rollin's book, 
  9.     "Macintosh Programming Secrets" 2nd ed. from Chapter 7 which dealt with
  10.     windows.  I've modified the code to work with Hsoi's App Shell.  These modifictions
  11.     include (but are not limited to): converting variables from "pre-Copland" to
  12.     Copland (e.g. WindowPtr to WindowRef); using !OLDROUTINENAMES; using pre-Copland
  13.     accessor routines for ease of migration to Copland; writing and using
  14.     UniversalProcPtr's for function callbacks; any other necessary modifications
  15.     to allow both 68k and PPC native code; routine name changes for consistancy
  16.     with HAS (e.g. HsoiXXX()); any other code changes (removal, modification, etc.)
  17.     to function consistantly with HAS (e.g. a Windows menu adjuster routine); etc.
  18.     
  19.     One big change worthy of further explanation:  in the original Knaster/Rollin's
  20.     code, they maintained their window list by having each window's refCon field point
  21.     to the youngest/next window.  In HAS, we already use the window's refCon field
  22.     to store a reference to the DocumentRecord associated with that window.  So
  23.     to expand upon this, I've added a "nextWindow" member to the DocumentRecord
  24.     which takes the place of this.  to access the next window, we access the
  25.     current window's refCon to get the DocumentRecord, then get the next window
  26.     from there.  I wrote a couple functions to facilitate the setting and getting
  27.     of this reference.
  28.     
  29. */
  30.  
  31. #pragma mark ••• #includes •••
  32.  
  33. #ifndef _WASTE_
  34. #include "WASTE.h"
  35. #endif
  36. #include "HASGlobals.h"
  37. #ifndef __HSOIS_APP_SHELL__
  38. #include "HASMain.h"
  39. #endif
  40. #include "HASMenuWindows.h"
  41. #include "HASWindows.h"
  42. #include "HASUtilities.h"
  43.  
  44. #pragma mark -
  45. #pragma mark ••• Constants •••
  46.  
  47. const short    kOffscreenLocation = 0x4000;    // Halfway to infinity
  48.  
  49. #pragma mark -
  50. #pragma mark ••• Globals •••
  51.  
  52. // the following variables are only needed for functions "interal" to this file.
  53. // therefore, we'll just declare these "local-globals" here instead of in our main
  54. // globals file.
  55.  
  56. short        pMaxWindowsPerRow = 0;
  57. short        pMaxWindowsPerColumn = 0;
  58. short        pNumberOfColumns = 0;
  59. short        pNumberOfRows = 0;
  60.  
  61. //    Used when matching up windows to their menu items, and vice-versa.
  62.  
  63. short        gMenuItem = 0;
  64. Boolean        gDoneCounting = 0;
  65. WindowRef    gFoundWindow = nil;
  66. WindowRef    gTargetWindow = nil;
  67.  
  68.  
  69. // Variables set up by HsoiForEachWindowDo() and HsoiForEachWindowPerScreenDo()
  70.  
  71. short        gWindowNumber = 0;        //    Ordinal number of the current window on the current monitor.
  72.  
  73. short        gNumberOfWindows = 0;    //    Total number of windows on the current monitor.
  74.  
  75. GDHandle    gScreenDevice= nil;        //    Reference to the current monitor.
  76.  
  77. Rect        gScreenRect = {0,0,0,0}; //    Bounds of the current monitor. Takes into account the menubar
  78.                                 //    if this is the main monitor.
  79.  
  80.  
  81. // Variables set up and shared by tiling and stacking routines.
  82.  
  83. short        gScreenWidth = 0;        //    Width of the current monitor.
  84.  
  85. short        gScreenHeight = 0;        //    Height of the current monitor.
  86.  
  87. short        gNewWindowWidth = 0;    //    When tiling or stacking, every window is essentially the same
  88.                                 //    size. This variable holds the window’s new width.
  89.  
  90. short        gNewWindowHeight = 0;    //    Ditto, but for height.
  91.  
  92. // and our UPP's...
  93.  
  94. HsoiSetUpUPP            gTileSetupUPP = nil;
  95. HsoiWindowActionUPP        gTileTheWindowUPP = nil;
  96. HsoiSetUpUPP            gStackSetupUPP = nil;
  97. HsoiWindowActionUPP        gStackTheWindowUPP = nil;
  98. HsoiWindowActionUPP        gLookForSelectedWindowUPP = nil;
  99. HsoiWindowActionUPP        gCountWindowsUPP = nil;
  100. HsoiWindowActionUPP        gLookForPreviousWindowUPP = nil;
  101. HsoiWindowActionUPP        gCountSomeWindowUPP = nil;
  102.  
  103.  
  104. #pragma mark -
  105. #pragma mark ••• Window Tiling •••
  106.  
  107. // this is the function called by DoMenuCommand for Tiling windows
  108.  
  109.  
  110. /*******************************************************************************
  111.  
  112.     DoTileWindows
  113.  
  114.     Call ForEachWindowPerScreenDo, which is a routine that allows us to
  115.     perform a specified operation on every window. Our task here is to tile
  116.     all the windows, making sure that each is the same size, but in
  117.     non-overlapping locations. In our TileSetup routine, we calculate the
  118.     windows’ size and save it off. In our TileTheWindow routine, we set each
  119.     window to that size, and position it in the right place.
  120.  
  121. *******************************************************************************/
  122. void    HsoiDoTileWindows( void )
  123. {
  124.     HsoiForEachWindowPerScreenDo( gTileSetupUPP, gTileTheWindowUPP, nil );
  125.  
  126.     return;
  127. }
  128.  
  129.  
  130. //
  131. //    This function is called once per monitor by ForEachWindowPerScreenDo.
  132. //    Based on the size of the screen and the number of windows that are to
  133. //    be tiled on the screen, we determine the number of rows and columns
  134. //    that we want to tile the windows into. We also determine the size
  135. //    each window on this screen should be.
  136. //
  137. void    HsoiTileSetup( void )
  138. {
  139.     if ( gNumberOfWindows > 0 )
  140.     {
  141.         // the calculations in here should be pretty self-explanitory
  142.         
  143.         gScreenRect.top += kGapBetweenWindows;
  144.         gScreenRect.left += kGapBetweenWindows;
  145.  
  146.         gScreenWidth = gScreenRect.right - gScreenRect.left;
  147.         gScreenHeight = gScreenRect.bottom - gScreenRect.top;
  148.  
  149.         pMaxWindowsPerRow = gScreenWidth / kMinWidth;
  150.         pMaxWindowsPerColumn = gScreenHeight / kMinHeight;
  151.  
  152.         pNumberOfColumns = (gNumberOfWindows - 1) / pMaxWindowsPerColumn + 1;
  153.         pNumberOfRows = (gNumberOfWindows - 1) / pNumberOfColumns + 1;
  154.  
  155.         gNewWindowWidth = gScreenWidth / pNumberOfColumns;
  156.         gNewWindowHeight = gScreenHeight / pNumberOfRows;
  157.     }
  158.     
  159.     return;
  160. }
  161.  
  162. //
  163. //    Main window cruncher for tiling. This routine is called once for every
  164. //    window. Makes the given window the size we determined in TileSetUp, and
  165. //    place it in the appropriate row and column based on its ordering in the
  166. //    window list.
  167. //
  168. //    Note that our calculations are set up to determine the size and location
  169. //    of the windows _frame_, not its content rectangle (i.e., its portRect).
  170. //    However, MoveWindow and SizeWindow like to work with values that _do_
  171. //    affect the portRect. To solve this problem, we do the actual moving and
  172. //    resizing by creating a routine called SetWindowBounds. This utility
  173. //    routine translates the frame rectangle into the content rectangle for us
  174. //    before calling MoveWindow and SizeWindow.
  175. //
  176. void    HsoiTileTheWindow(WindowRef theWindow)
  177. {
  178.     Rect    newBounds;
  179.     short    row, column;
  180.  
  181.     // the calculations here should be pretty self-explanitory
  182.     
  183.     row = (gWindowNumber - 1) / pNumberOfColumns;
  184.     column = (gWindowNumber - 1) % pNumberOfColumns;
  185.  
  186.     newBounds.left        = gScreenRect.left + column * gNewWindowWidth;
  187.     newBounds.top        = gScreenRect.top + row * gNewWindowHeight;
  188.     newBounds.right        = newBounds.left + gNewWindowWidth - kGapBetweenWindows;
  189.     newBounds.bottom    = newBounds.top + gNewWindowHeight - kGapBetweenWindows;
  190.  
  191.     // set the window's bounds
  192.     
  193.     HsoiSetWindowBounds(theWindow, newBounds);
  194.     
  195.     // and readjust the window so it looks right
  196.     
  197.     HsoiReadjustAfterStackTile(theWindow );
  198.     
  199.     return;
  200. }
  201.  
  202.  
  203. #pragma mark -
  204. #pragma mark ••• Window Stacking •••
  205.  
  206. // this is the function called by DoMenuCommand to stack the windows
  207.  
  208. /*******************************************************************************
  209.  
  210.     DoStackWindows
  211.  
  212.     Call ForEachWindowPerScreenDo, which is a routine that allows us to
  213.     perform a specified operation on every window. Our task here is to stack
  214.     all the windows. In our StackSetup routine, we calculate the number of
  215.     diagonals we’ll have on the monitor we are tiling on. We also calculate an
  216.     initial window size, a size which gets smaller as subsequent windows get
  217.     stacked further down and to the right. In our TileTheWindow routine, we
  218.     set each window to the appropriate size and position.
  219.  
  220. *******************************************************************************/
  221. void    HsoiDoStackWindows( void )
  222. {
  223.  
  224.     HsoiForEachWindowPerScreenDo( gStackSetupUPP, gStackTheWindowUPP, nil );
  225.     
  226.     return;
  227. }
  228.  
  229.  
  230.  
  231. //
  232. //    This function is called once per monitor by ForEachWindowPerScreenDo.
  233. //    Its purpose is two-fold. First, it figures out how many diagonals of
  234. //    windows will be needed to stack all of the windows on the current screen.
  235. //    Right now, we permit only 8 windows to be stacked in a diagonal before we
  236. //    need to create a new diagonal of windows that’s move over to the right a
  237. //    little bit.
  238. //
  239. //    The second thing this function does is figure out a good initial size for
  240. //    the stacked windows. This size shrinks for windows that are stacked
  241. //    further down and to the right on the screen. The calculations for this
  242. //    shrinking are in the StackTheWindow function.
  243. //
  244. void        HsoiStackSetup( void )
  245. {
  246.     short    maxWindowsInDiagonal;
  247.     short    numberOfBottomWindows;
  248.  
  249.     InsetRect(&gScreenRect, kGapBetweenWindows, kGapBetweenWindows);
  250.  
  251.     // Find the height and width of this window
  252.  
  253.     gScreenWidth = gScreenRect.right - gScreenRect.left;
  254.     gScreenHeight = gScreenRect.bottom - gScreenRect.top;
  255.  
  256.     // Find the longest diagonal of windows we will be dealing
  257.     // with. This will either be kWindowsPerDiagonal or
  258.     // gNumberOfWindows, whichever is smaller.
  259.  
  260.     maxWindowsInDiagonal = gNumberOfWindows;
  261.     if (maxWindowsInDiagonal > kWindowsPerDiagonal)
  262.         maxWindowsInDiagonal = kWindowsPerDiagonal;
  263.  
  264.     // Find out how many windows will end up in the last
  265.     // position of a diagonal. This number is crucial in
  266.     // determining the horizontal span of all the windows,
  267.     // which is used in calculating the windows’ width.
  268.  
  269.     numberOfBottomWindows = (gNumberOfWindows / kWindowsPerDiagonal);
  270.     if (numberOfBottomWindows > 0)
  271.         --numberOfBottomWindows;
  272.  
  273.     // Figure out the size of the first window to be stacked.
  274.     // We start off with the the size of the entire screen and
  275.     // start trimming it back to allow for other windows. First,
  276.     // we trim back the width by an amount equal to the number
  277.     // of pixels that the rightmost window will be from the
  278.     // left edge of the screen. Next, we trim back the height so
  279.     // that we can accommodate the tallest diagonal we’ll be dealing
  280.     // with.
  281.  
  282.     gNewWindowWidth = gScreenWidth
  283.                         - (((maxWindowsInDiagonal - 1) + numberOfBottomWindows)
  284.                             * kHorizontalStagger);
  285.     gNewWindowHeight = gScreenHeight
  286.                         - (maxWindowsInDiagonal - 1) * kHorizontalStagger;
  287.     
  288.     return;
  289. }
  290.  
  291.  
  292. //
  293. //    This routine is called once for each window. Its function is to calculate
  294. //    the size and location for each window. First, it determines which diagonal
  295. //    the specified window should be in and its position within that diagonal.
  296. //    Then it determines the horizontal position for the window; as each window
  297. //    is always moved to the right from the previous window by a constant
  298. //    amount, the horizontal position is a simple function of the window’s
  299. //    creation rank. Next, we determine the vertical position of the window.
  300. //    This, too, is a simple function, this time of the window’s position in the
  301. //    diagonal it will be placed in. What this means is that the 1st, 9th, 17th,
  302. //    etc., windows will all have the same vertical position. Similarly for the
  303. //    2nd, 10th, 18th, etc., windows, and so on.
  304. //
  305. //    Next, we have to determine the size of the window. First, we consider the
  306. //    width. Each window in a diagonal is the same size. This size is determined
  307. //    by taking the initial window size calculated in StackSetup, and
  308. //    subtracting an amount that is a function of the diagonal number we are
  309. //    working on. First, we subtract an amount such that the right edge of each
  310. //    window in this diagonal will line up with the corresponding window in the
  311. //    previous diagonal. Next, we add back 10 pixels just to give a nice
  312. //    staggering effect on the right hand side as well as the left. After doing
  313. //    that, we work on figuring the height of the window. We start off with the
  314. //    standard height calculated in StackSetup. Then we subtract a multiple of
  315. //    10 pixels that is based on how far down in the diagonal the window is.
  316. //    This means that the height of the window is a function of the window’s
  317. //    position in the diagonal, while the width of the window is a function of
  318. //    the position of the diagonal itself.
  319. //
  320. void        HsoiStackTheWindow(WindowRef theWindow)
  321. {
  322.     Rect    newBounds;
  323.     short    placeInDiagonal, diagonal;
  324.  
  325.     placeInDiagonal = (gWindowNumber - 1) % kWindowsPerDiagonal;
  326.     diagonal = (gWindowNumber - 1) / kWindowsPerDiagonal;
  327.  
  328.     newBounds.left        = gScreenRect.left + (gWindowNumber - 1) * kHorizontalStagger;
  329.     newBounds.top        = gScreenRect.top + placeInDiagonal * kVerticalStagger;
  330.     newBounds.right        = newBounds.left + gNewWindowWidth - diagonal * (kWindowsPerDiagonal * kHorizontalStagger - 10);
  331.     newBounds.bottom    = newBounds.top + gNewWindowHeight - placeInDiagonal * 10;
  332.  
  333.     HsoiSetWindowBounds(theWindow, newBounds);
  334.     
  335.     HsoiReadjustAfterStackTile(theWindow);
  336.     
  337.     return;
  338. }
  339.  
  340.  
  341. #pragma mark -
  342. #pragma mark ••• Windows Menu Handlers •••
  343.  
  344. // this is called by DoMenuCommand when a specific window is chosen fromthe menu
  345.  
  346.  
  347. /*******************************************************************************
  348.  
  349.     DoSelectFromWindowsMenu
  350.  
  351.     Called when the user selects one of the menu items containing a window’s
  352.     name (in the Windows menu). We stash off the menu item that was selected,
  353.     and then start iterating over all the windows in chronological order.
  354.     Since the list of window names in the Windows menu is also maintained in
  355.     chronological order, we know that there is a direct relationship between
  356.     the menu item number and the window’s chronological rank. In other words,
  357.     if we selected the Xth item in the Windows menu (not including the Tile or
  358.     Stack menu items), all we have to do is find the Xth oldest window and
  359.     select it.
  360.  
  361. *******************************************************************************/
  362. void    HsoiDoSelectFromWindowsMenu(short menuItem)
  363. {    
  364.     gMenuItem = menuItem - iFirstWindow + 1;
  365.     
  366.     HsoiForEachWindowDo( nil, gLookForSelectedWindowUPP, nil );
  367.     
  368.     
  369.     return;
  370. }
  371.  
  372.  
  373. void    HsoiLookForSelectedWindow(WindowRef theWindow)
  374. {
  375.     if (--gMenuItem == 0)
  376.         SelectWindow(theWindow);
  377.     
  378.     return;
  379. }
  380.  
  381.  
  382. /*******************************************************************************
  383.  
  384.     AddWindowToMenu
  385.  
  386.     Called to add a newly created window to the Windows menu. We make that
  387.     addition by making a call to AppendMenu to create the new menu item, and
  388.     then SetItem to set that item’s name. The reason why we don’t let
  389.     AppendMenu set the name is because of the meta-character processing that
  390.     AppendMenu performs. If our window’s name happened to have something like
  391.     a “!” or “/” it it, what appeared in the menu wouldn’t be what we
  392.     expected. SetItem doesn’t do recognize the meta-characters, so we avoid
  393.     that problem when calling it.
  394.  
  395.     Next, we insert our window into our own private window list. Normally, you
  396.     don’t need to do this. The Window Manager keeps track of all the windows
  397.     on the screen so that you don’t have to. However, the list it manages
  398.     links the windows in front to back order. In other words, the frontmost
  399.     window is at the front of the list, the record for the window behind it is
  400.     second in the list, and so on.
  401.  
  402.     In order to manage our Windows menu, we’d really like to have a list of
  403.     windows in chronological order. We do this by using the refCon field of
  404.     the window record. Each window’s refCon field will point to the next
  405.     youngest window. The oldest window (the head of the chain) will be kept in
  406.     the global variable gFirstWindow. The refCon field of the last window is
  407.     nil. If there are no windows, gFirstWindow is nil.
  408.  
  409. *******************************************************************************/
  410. void    HsoiAddWindowToMenu(WindowRef theWindow)
  411. {
  412.     Str255        title;
  413.     MenuRef        windowsMenu;
  414.     WindowRef    lastWindow;
  415.  
  416.     // get the title of the window to stick into the window's menu as the item
  417.     
  418.     GetWTitle(theWindow, title);
  419.     
  420.     // get a handle to the window's menu
  421.     
  422.     windowsMenu = GetMenu(mWindows);
  423.     
  424.     // stick something in there, but not our actual text just in case of meta characters
  425.     
  426.     AppendMenu(windowsMenu, "\pNeed something here or call doesn’t work.");
  427.     
  428.     // and now properly give the menu item the right text (again, meta characters)
  429.     
  430.     SetMenuItemText( windowsMenu, CountMenuItems(windowsMenu), title );
  431.     
  432.     // and now update our linked list of windows.  add this one to the list
  433.     
  434.     lastWindow = HsoiGetPreviouslyCreatedWindow(nil);    /* nil means “get last window” */
  435.     if (lastWindow != nil)
  436.         HsoiSetNextWindow( lastWindow, (long)theWindow );
  437.     else
  438.         gFirstWindow = theWindow;
  439.     
  440.     return;
  441. }
  442.  
  443.  
  444. /*******************************************************************************
  445.  
  446.     RemoveWindowFromMenu
  447.  
  448.     Get the menu item that corresponds to this window, and remove that menu
  449.     item from the Windows menu. Remove the window from our chronological list
  450.     of windows.
  451.  
  452. *******************************************************************************/
  453. void    HsoiRemoveWindowFromMenu(WindowRef theWindow)
  454. {
  455.     MenuRef        windowsMenu;
  456.     WindowRef    previousWindow;
  457.  
  458.     // get a handle to the windows menu
  459.     
  460.     windowsMenu = GetMenu(mWindows);
  461.     
  462.     // remove the menu item
  463.     
  464.     DeleteMenuItem(windowsMenu, HsoiGetMenuItemForWindow(theWindow));
  465.  
  466.     // and update our linked list of windows by removing this one from the list
  467.     
  468.     if (theWindow == gFirstWindow) {
  469.         gFirstWindow = (WindowRef)HsoiGetNextWindow(theWindow);
  470.     } else {
  471.         previousWindow = HsoiGetPreviouslyCreatedWindow(theWindow);
  472.         HsoiSetNextWindow( previousWindow, HsoiGetNextWindow(theWindow) );
  473.     }
  474.  
  475.     HsoiSetNextWindow( theWindow, 0 );
  476.     
  477.     return;
  478. }
  479.  
  480.  
  481. /*******************************************************************************
  482.  
  483.     GetMenuItemForWindow
  484.  
  485.     Given a window pointer, return the number for the Windows menu item that
  486.     corresponds to it. This is done by iterating over all our windows in
  487.     chronological order, incrementing a counter as we go. When we get to the
  488.     window we are interested in, we stop counting. This means that our counter
  489.     ends up hold the chronological rank of our window. This rank is then added
  490.     to iFirstWindow to give us the appropriate menu item number.
  491.  
  492. *******************************************************************************/
  493.  
  494. short    HsoiGetMenuItemForWindow(WindowRef theWindow)
  495. {    
  496.     gDoneCounting = false;
  497.     gTargetWindow = theWindow;
  498.     gMenuItem = iFirstWindow - 1;    
  499.     
  500.     HsoiForEachWindowDo( nil, gCountSomeWindowUPP, nil );
  501.  
  502.     return gMenuItem;
  503. }
  504.  
  505.  
  506. // this adjusts our Windows menu.  this was not part of the original Knaster/Rollins
  507. // code.
  508.  
  509. void    HsoiAdjustWindowsMenu( WindowRef window )
  510. {
  511.     short            menuItem;
  512.     short            i;
  513.     MenuRef            windowsMenu = GetMenuHandle( mWindows );
  514.  
  515.     // first, remove all the check marks from the menu
  516.     
  517.     for ( i = 1; i<= CountMenuItems( windowsMenu ); i++ )
  518.         CheckItem( windowsMenu, i, false );
  519.     
  520.     // if we have a NIL window, or a window that's not a document window (cause
  521.     // this menu only tracks document windows), return...nothing else to do
  522.     
  523.     if ( !HsoiIsDocumentWindow( window ) )
  524.         return;
  525.     
  526.     // find out which menu item is the one for our window
  527.     
  528.     menuItem = HsoiGetMenuItemForWindow( window );
  529.     
  530.     // and put a check mark by that window
  531.     
  532.     CheckItem( windowsMenu, menuItem, true );
  533.     
  534.     // now, set the item's menu command (this is sorta just "cosmetic") 
  535.     
  536.     for ( i = iFirstWindow; i <= CountMenuItems(windowsMenu); i++ )
  537.     {
  538.         // we only want menu commands for the first 9 items (allows cmd-1 to cmd-9)
  539.         if ( i >= (iFirstWindow + 9) )
  540.             break;
  541.         
  542.         // and this sets the cmd...the funky math converts the counter to the ASCII
  543.         // (decimal) equivs ( number 1 is ASCII 49 (decimal))
  544.         
  545.         SetItemCmd( windowsMenu, i, (char)(i - iFirstWindow + 1 + 48) );
  546.     }
  547.     
  548.     return;
  549. }
  550.  
  551. #pragma mark -
  552. #pragma mark ••• Window Iteraters •••
  553.  
  554. /*******************************************************************************
  555.  
  556.     ForEachWindowDo
  557.  
  558.     Routine that iterates over all of the windows. It first counts up the
  559.     number of windows on the monitor we are currently examining. Next, it
  560.     calls a setup routine provided by the caller. After that, it calls an
  561.     action procedure for each window. This action procedure is passed a
  562.     pointer to the current window we are iterating over. It is also able to
  563.     access the total number of windows and the window’s rank through the
  564.     global variables gNumberOfWindows and gWindowNumber. After all windows
  565.     have been acted upon, a clean up routine provided by the caller is called.
  566.  
  567.     Note that windows are iterated in chronological order, as maintained by
  568.     the links stored in the their refCon fields.
  569.  
  570. *******************************************************************************/
  571. void    HsoiForEachWindowDo(HsoiSetUpUPP theStarter,
  572.                         HsoiWindowActionUPP theDoer,
  573.                         HsoiFinishUpUPP enderWiggin)
  574.  
  575. {
  576.     WindowRef    currentWindow;
  577.     Boolean        windowIsOurConcern;
  578.  
  579.     // To get the number of windows, we recursively call ourself
  580.     // with an action procedure that simply increments a counter.
  581.     // So that we don’t infinitely recurse, we check the action
  582.     // procedure to see if it’s our counting routine. If not,
  583.     // we don’t recurse.
  584.  
  585.     if (theDoer != gCountWindowsUPP )
  586.     {        
  587.         gNumberOfWindows = 0;
  588.         
  589.         HsoiForEachWindowDo( nil, gCountWindowsUPP, nil );
  590.     }
  591.  
  592.     if ((theDoer == gCountWindowsUPP) || (gNumberOfWindows > 0)) {
  593.     
  594.         // Start keeping track of window rank
  595.     
  596.         gWindowNumber = 0;
  597.     
  598.         // If the caller provided a setup routine, call it.
  599.     
  600.         if (theStarter != nil)
  601.             CallHsoiSetUpProc( theStarter );
  602.     
  603.         // Start iterating over all the windows. Skip over any DA’s or
  604.         // dialog windows. The expression that sets “windowIsOurConcern”
  605.         // could be modified to also skip over invisible windows.
  606.     
  607.         if (theDoer != nil) {
  608.             currentWindow = gFirstWindow;
  609.             while (currentWindow != nil) {
  610.                 windowIsOurConcern = HsoiIsDocumentWindow(currentWindow);
  611.                 if (windowIsOurConcern) {
  612.                     ++gWindowNumber;
  613.                     CallHsoiWindowActionProc( theDoer, currentWindow );
  614.                 }
  615.                 currentWindow = (WindowRef)HsoiGetNextWindow(currentWindow);
  616.             }
  617.         }
  618.     
  619.         // Done with all the windows. If the caller
  620.         // provided a cleanup routine, call it.
  621.     
  622.         if (enderWiggin != nil)
  623.             CallHsoiFinishUpProc( enderWiggin );
  624.     }
  625.  
  626.     return;
  627. }
  628.  
  629.  
  630. /*******************************************************************************
  631.  
  632.     ForEachWindowPerScreenDo
  633.  
  634.     Ugly-ass routine that iterates over all of the windows, but in a peculiar
  635.     fashion. What it does is first iterate over all the monitors hooked up to
  636.     the machine. For each monitor, it first counts up the number of windows on
  637.     the monitor we are currently examining. Next, it calls a setup routine
  638.     provided by the caller. After that, it calls an action procedure for each
  639.     window on the monitor. This action procedure is passed a pointer to the
  640.     current window we are iterating over. It is also able to access the handle
  641.     to the current monitor, the size of the current monitor, the number of
  642.     windows on the current monitor, and the window’s rank on the current
  643.     monitor through the global variables gScreenDevice, gScreenRect,
  644.     gNumberOfWindows, and gWindowNumber. After all windows on the monitor have
  645.     been acted upon, a clean up routine provided by the caller is called.
  646.     Finally, we move on to the next monitor.
  647.  
  648.     Note that windows are iterated in chronological order, as maintained by
  649.     the links stored in the their refCon fields.
  650.  
  651. *******************************************************************************/
  652. void    HsoiForEachWindowPerScreenDo(HsoiSetUpUPP theStarter,
  653.                                     HsoiWindowActionUPP theDoer,
  654.                                     HsoiFinishUpUPP enderWiggin)
  655.  
  656. {
  657.     WindowRef    currentWindow;
  658.     Boolean        windowIsOurConcern;
  659.     GDHandle    currentScreenDevice;
  660.  
  661.     if (gHasColorQD)
  662.         currentScreenDevice = GetDeviceList();
  663.     else
  664.         currentScreenDevice = nil;
  665.  
  666.     do {
  667.  
  668.         // To get the number of windows on the current monitor of
  669.         // interest, we recursively call ourself with an action
  670.         // procedure that simply increments a counter. So that we
  671.         // don’t infinitely recurse, we check the action procedure
  672.         // to see if it’s our counting routine. If not, we don’t recurse.
  673.  
  674.         if (theDoer != gCountWindowsUPP)
  675.         {            
  676.             gNumberOfWindows = 0;
  677.             
  678.             HsoiForEachWindowPerScreenDo( nil, gCountWindowsUPP, nil );
  679.         }
  680.  
  681.         if ((theDoer == gCountWindowsUPP) || (gNumberOfWindows > 0)) {
  682.             // Start keeping track of window rank on this monitor
  683.     
  684.             gWindowNumber = 0;
  685.     
  686.             // Export the handle to the current device
  687.     
  688.             gScreenDevice = currentScreenDevice;
  689.     
  690.             // Export the size of the monitor. If we have a valid device
  691.             // handle, use it to get the device’s size. If we don’t have
  692.             // a valid handle (it is nil -- indicating that we are running
  693.             // on a machine without Color QuickDraw), get the size of the
  694.             // screen from GetMainScreenRect() (which will effectively
  695.             // return qd.screenBits.bounds in this case).
  696.     
  697.             if (currentScreenDevice != nil) {
  698.                 gScreenRect = (*currentScreenDevice)->gdRect;
  699.  
  700. //•• this doesn't make sense...shouldn't it be if current == GetMainDevice()?
  701. // that seems most logical to me...unfortunately, i can't test this too much
  702. // cause i don't have a multiple monitor setup.
  703.  
  704.                 if (currentScreenDevice == GetMainDevice())
  705. //                currentScreenDevice = GetMainDevice();
  706. //                if ( currentScreenDevice )                
  707.                     gScreenRect.top += LMGetMBarHeight();
  708.             } else {
  709.                 gScreenRect = HsoiGetMainScreenRect();
  710.                 gScreenRect.top += LMGetMBarHeight();
  711.             }
  712.     
  713.             // If the caller provided a setup routine, call it.
  714.     
  715.             if (theStarter != nil)
  716.                 CallHsoiSetUpProc( theStarter );
  717.     
  718.             // Start iterating over all the windows. If the majority of the
  719.             // windows is on the monitor we are currently looking at from
  720.             // our outer loop, pass that window to the caller’s action
  721.             // procedure. Skip over any DA’s or dialog windows. The expression
  722.             // that sets “windowIsOurConcern” could be modified to also skip
  723.             // over invisible windows.
  724.     
  725.             if (theDoer != nil) {
  726.                 currentWindow = gFirstWindow;
  727.                 while (currentWindow != nil) {
  728.                     windowIsOurConcern = HsoiIsDocumentWindow(currentWindow) &&
  729.                             (currentScreenDevice == HsoiGetWindowDevice(currentWindow));
  730.  
  731.                     if (windowIsOurConcern) {
  732.                         ++gWindowNumber;
  733.                         CallHsoiWindowActionProc( theDoer, currentWindow );
  734.                     }
  735.                     currentWindow = (WindowRef)HsoiGetNextWindow(currentWindow);
  736.                 }
  737.             }
  738.     
  739.             // Done with all the windows on this monitor. If the caller
  740.             // provided a cleanup routine, call it.
  741.     
  742.             if (enderWiggin != nil)
  743.                 CallHsoiFinishUpProc( enderWiggin );
  744.         }
  745.  
  746.         // Do the next monitor
  747.  
  748.         if (gHasColorQD)
  749.             currentScreenDevice = GetNextDevice(currentScreenDevice);
  750.  
  751.     } while (currentScreenDevice != nil);
  752.     
  753.     return;
  754. }
  755.  
  756. #pragma mark -
  757. #pragma mark ••• Other Window Procs •••
  758.  
  759. /*******************************************************************************
  760.  
  761.     CountWindows
  762.  
  763.     This is a WindowActionProc called by ForEachWindowPerScreenDo and
  764.     ForEachWindowDo to count windows.
  765.  
  766. *******************************************************************************/
  767. void    HsoiCountWindows(WindowRef theWindow)
  768. {
  769. #pragma unused ( theWindow )
  770.  
  771.     ++gNumberOfWindows;
  772.     
  773.     return;
  774. }
  775.  
  776.  
  777. /*******************************************************************************
  778.  
  779.     GetPreviouslyCreatedWindow
  780.  
  781.     Called to return the window immediately before the target window in our
  782.     chronological list of windows. This is handy for searching backwards when
  783.     working with a singly linked list of records.
  784.  
  785. *******************************************************************************/
  786. WindowRef    HsoiGetPreviouslyCreatedWindow(WindowRef theWindow)
  787. {    
  788.     gFoundWindow = nil;
  789.     gTargetWindow = theWindow;
  790.     HsoiForEachWindowDo( nil, gLookForPreviousWindowUPP, nil );
  791.     
  792.     return gFoundWindow;
  793. }
  794.  
  795. void    HsoiLookForPreviousWindow(WindowRef theWindow)
  796. {
  797.     if ( gTargetWindow == (WindowRef)HsoiGetNextWindow(theWindow))
  798.         gFoundWindow = theWindow;
  799.     
  800.     return;
  801. }
  802.  
  803.  
  804. void    HsoiCountSomeWindow(WindowRef theWindow)
  805. {
  806.     if (!gDoneCounting) {
  807.         ++gMenuItem;
  808.         if (theWindow == gTargetWindow)
  809.             gDoneCounting = true;
  810.     }
  811.     
  812.     return;
  813. }
  814.  
  815. #pragma mark -
  816. #pragma mark ••• Window Utilities •••
  817.  
  818.  
  819.  
  820. /*******************************************************************************
  821.  
  822.     SetWindowBounds
  823.  
  824.     Set the size and location of the given window’s FRAME. Note that this
  825.     differs from a simple MoveWindow/SizeWindow combination in that the
  826.     parameters passed to those routines work on the window’s content
  827.     rectangle, not the outer frame rectangle.
  828.  
  829.     What we do to accomplish this is to take the frame rectangle passed into
  830.     this routine and calculate what the content rectangle would be for a
  831.     window that had that frame’s size. This is done by looking at the
  832.     window’s current strucRgn (the region that describes the window’s frame)
  833.     and contRgn (the region that describes the inside content area of the
  834.     window). The difference in size between these two regions is determined,
  835.     and is then applied to “newBounds”. This gives us a rectangle that can be
  836.     passed to MoveWindow and SizeWindow.
  837.  
  838.     Note that we first hide the window before changing its bounds. This is so
  839.     that we don’t first see the effect of MoveWindow, and then the effect of
  840.     SizeWindow.
  841.  
  842. *******************************************************************************/
  843. void        HsoiSetWindowBounds(WindowRef theWindow, Rect newBounds)
  844. {
  845.     short        top;
  846.     short        left;
  847.     short        height;
  848.     short        width;
  849.  
  850.     short        topInset;
  851.     short        leftInset;
  852.     short        bottomInset;
  853.     short        rightInset;
  854.  
  855.     Rect        oldBounds;
  856.  
  857.     // always make sure you initialize a Region before you use it!
  858.     
  859.     RgnHandle    contRgn = NewRgn();
  860.     RgnHandle    structRgn = NewRgn();
  861.  
  862.     // these Copland accessor functions make life so much nicer...
  863.     
  864.     GetWindowContentRgn( theWindow, contRgn );
  865.     GetWindowStructureRgn( theWindow, structRgn );
  866.  
  867.     // get the old bounds
  868.     
  869.     oldBounds = (*structRgn)->rgnBBox;
  870.  
  871.     // and if the old and new bounds aren't the same, let's set it
  872.     
  873.     if (!EqualRect(&oldBounds, &newBounds)) {
  874.  
  875.         // do some calculations
  876.         
  877.         topInset    = (*contRgn)->rgnBBox.top        - (*structRgn)->rgnBBox.top;
  878.         leftInset    = (*contRgn)->rgnBBox.left        - (*structRgn)->rgnBBox.left;
  879.         bottomInset    = (*structRgn)->rgnBBox.bottom    - (*contRgn)->rgnBBox.bottom;
  880.         rightInset    = (*structRgn)->rgnBBox.right    - (*contRgn)->rgnBBox.right;
  881.  
  882.         top = newBounds.top + topInset;
  883.         left = newBounds.left + leftInset;
  884.         height = newBounds.bottom - top - bottomInset;
  885.         width = newBounds.right - left - rightInset;
  886.  
  887.         // hide the window (so the user doesn't have to see our manipulations
  888.         
  889.         HideWindow(theWindow);
  890.         
  891.         // move the window to it's new location
  892.         
  893.         MoveWindow(theWindow, left, top, false);
  894.         
  895.         // resize it to its new size
  896.         
  897.         SizeWindow(theWindow, width, height, true);
  898.         
  899.         // select it
  900.         
  901.         SelectWindow(theWindow);
  902.         
  903.         // and show it again all new and happy
  904.         
  905.         ShowWindow(theWindow);
  906.     }
  907.     
  908.     // of course, don't forget to dispose of your regions after you're done using
  909.     // them, else risk memory leaks!
  910.     
  911.     if ( contRgn != nil )
  912.         DisposeRgn( contRgn );
  913.     if ( structRgn != nil )
  914.         DisposeRgn( structRgn );
  915.     
  916.     return;
  917. }
  918.  
  919.  
  920. /*******************************************************************************
  921.  
  922.     GetWindowContentRect
  923.  
  924.     Given a window pointer, return the global rectangle that encloses the
  925.     content area of the window.
  926.  
  927. *******************************************************************************/
  928. Rect    HsoiGetWindowContentRect(WindowRef window)
  929. {
  930.     WindowRef    oldPort;
  931.     Rect        contentRect;
  932.  
  933.     GetPort(&oldPort);
  934.     SetPortWindowPort(window);
  935.     
  936.     // it's a nice thing that a window's portRect and contentRect's are the
  937.     // same things.
  938.     contentRect = GetWindowPort(window)->portRect;
  939.     HsoiLocalToGlobalRect(&contentRect);
  940.     SetPort(oldPort);
  941.     return contentRect;
  942. }
  943.  
  944.  
  945. /*******************************************************************************
  946.  
  947.     GetWindowStructureRect
  948.  
  949.     This procedure is used to get the rectangle that surrounds the entire
  950.     structure of a window. This works whether or not the window is visible. If
  951.     the window is visible, it is a simple matter of using the bounding
  952.     rectangle of the structure region. If the window is invisible, the
  953.     strucRgn is not valid. To make it valid, the window has to be moved way
  954.     off the screen and then made visible. This generates a valid strucRgn,
  955.     although it is valid for the position that is way off the screen. It still
  956.     needs to be offset back into the original position. Once the bounding
  957.     rectangle for the strucRgn is obtained, the window can then be hidden
  958.     again and moved back to its correct location. Note that ShowHide is used,
  959.     instead of ShowWindow and HideWindow. HideWindow can change the plane of
  960.     the window. Also, ShowHide does not affect the hiliting of windows.
  961.  
  962. *******************************************************************************/
  963. Rect    HsoiGetWindowStructureRect(WindowRef window)
  964. {
  965.     GrafPtr        oldPort;
  966.     Rect        structureRect;
  967.     Point        windowLoc;
  968.     RgnHandle    structRgn = NewRgn();
  969.     
  970.     if ( IsWindowVisible( window ) )
  971.     {
  972.         // if it's visible, just get it
  973.         
  974.         GetWindowStructureRgn( window, structRgn );
  975.         structureRect = (*structRgn)->rgnBBox;
  976.     }
  977.     else
  978.     {
  979.         // it's not visible, so let's move it offscreen to get it
  980.         
  981.         GetPort(&oldPort);
  982.         SetPortWindowPort(window);
  983.         windowLoc = HsoiGetGlobalTopLeft(window);
  984.         
  985.         // move it offscreen...way off screen
  986.         
  987.         MoveWindow(window, windowLoc.h, kOffscreenLocation, false);
  988.         
  989.         // show the window.  look up ShowHide and see why it's nice to use in this context
  990.         
  991.         ShowHide(window, true);
  992.         
  993.         // get the struct rect
  994.         
  995.         GetWindowStructureRgn( window, structRgn );
  996.         structureRect = (*structRgn)->rgnBBox;
  997.         
  998.         // hide the window again
  999.         
  1000.         ShowHide(window, false);
  1001.         
  1002.         // move it back onscreen
  1003.         
  1004.         MoveWindow(window, windowLoc.h, windowLoc.v, false);
  1005.         OffsetRect(&structureRect, 0, windowLoc.v - kOffscreenLocation);
  1006.         SetPort(oldPort);
  1007.     }
  1008.     
  1009.     // and dispose of our region for no memory leaks.
  1010.     
  1011.     if ( structRgn != nil )
  1012.         DisposeRgn( structRgn );
  1013.     
  1014.     return structureRect;
  1015. }
  1016.  
  1017.  
  1018. /*******************************************************************************
  1019.  
  1020.     GetWindowDeviceRectNMB (No Menu Bar)
  1021.  
  1022.     Given a window pointer, find the device that contains most of the window
  1023.     and return that device’s bounding rectangle. If this device is the main
  1024.     device, remove the menubar area from the rectangle.
  1025.  
  1026. *******************************************************************************/
  1027. Rect    HsoiGetWindowDeviceRectNMB(WindowRef window)
  1028. {
  1029.     Rect        deviceRect, tempRect;
  1030.  
  1031.     deviceRect = HsoiGetWindowDeviceRect(window);
  1032.     tempRect = HsoiGetMainScreenRect();
  1033.     if (EqualRect(&deviceRect, &tempRect))
  1034.         deviceRect.top += LMGetMBarHeight();
  1035.  
  1036.     return deviceRect;
  1037. }
  1038.  
  1039.  
  1040. /*******************************************************************************
  1041.  
  1042.     GetWindowDeviceRect
  1043.  
  1044.     Given a window pointer, find the device that contains most of the window
  1045.     and return that device’s bounding rectangle.
  1046.  
  1047. *******************************************************************************/
  1048. Rect    HsoiGetWindowDeviceRect(WindowRef window)
  1049. {
  1050.     if (gHasColorQD)
  1051.         return (*HsoiGetWindowDevice(window))->gdRect;
  1052.     else
  1053.         return HsoiGetMainScreenRect();
  1054. }
  1055.  
  1056.  
  1057. /*******************************************************************************
  1058.  
  1059.     GetWindowDevice
  1060.  
  1061.     Given a window pointer, find the device that contains most of the window
  1062.     and return its handle.
  1063.  
  1064. *******************************************************************************/
  1065. GDHandle    HsoiGetWindowDevice(WindowRef window)
  1066. {
  1067.     return HsoiGetRectDevice(HsoiGetWindowStructureRect(window));
  1068. }
  1069.  
  1070.  
  1071. #pragma mark -
  1072. #pragma mark ••• Other Utilities •••
  1073.  
  1074. /*******************************************************************************
  1075.  
  1076.     GetRectDevice
  1077.  
  1078.     Given a rectangle in global coordinates, find the monitor it overlaps the
  1079.     most. This is done by iterating over all of the monitors with the
  1080.     GetDeviceList and GetNextDevice calls. For each device, we find the area
  1081.     of overlap with the given rectangle. The device that results in the
  1082.     greatest overlap is returned to the caller.
  1083.  
  1084.     This routine assumes the existence of the Graphics Device Manager.
  1085.  
  1086. *******************************************************************************/
  1087. GDHandle    HsoiGetRectDevice(Rect globalRect)
  1088. {
  1089.     long        area;
  1090.     long        maxArea;
  1091.     GDHandle    device;
  1092.     GDHandle    deviceToReturn;
  1093.     Rect        intersection;
  1094.  
  1095.     deviceToReturn = GetMainDevice();            /* Use as default choice. */
  1096.     maxArea = 0;
  1097.  
  1098.     for (device = GetDeviceList(); device != nil; device = GetNextDevice(device)) {
  1099.         if (TestDeviceAttribute(device, screenDevice)
  1100.           && TestDeviceAttribute(device, screenActive)
  1101.           && SectRect(&globalRect, &((*device)->gdRect), &intersection)) {
  1102.             area = (intersection.right - intersection.left) *
  1103.                     (intersection.bottom - intersection.top);
  1104.             if (area > maxArea) {
  1105.                 deviceToReturn = device;
  1106.                 maxArea = area;
  1107.             }
  1108.         }
  1109.     }
  1110.     return deviceToReturn;
  1111. }
  1112.  
  1113.  
  1114. /*******************************************************************************
  1115.  
  1116.     LocalToGlobalRect
  1117.  
  1118.     Convert a rectangle from local coordinates to global coordinates. Like
  1119.     QuickDraw’s LocalToGlobal, it assumes that the current port is set
  1120.     correctly.
  1121.  
  1122. *******************************************************************************/
  1123. void    HsoiLocalToGlobalRect(Rect *aRect)
  1124. {
  1125.     LocalToGlobal(&topLeft(*aRect));
  1126.     LocalToGlobal(&botRight(*aRect));
  1127.  
  1128.     return;
  1129. }
  1130.  
  1131.  
  1132. /*******************************************************************************
  1133.  
  1134.     GetGlobalTopLeft
  1135.  
  1136.     Return the top left point of the given window’s port in global
  1137.     coordinates. This returns the top left point of the window’s content area
  1138.     only; it doesn’t include the window’s drag region (or title bar).
  1139.  
  1140. *******************************************************************************/
  1141. Point    HsoiGetGlobalTopLeft(WindowRef window)
  1142. {
  1143.     GrafPtr            oldPort;
  1144.     Point            globalPt;
  1145.  
  1146.     GetPort(&oldPort);
  1147.     SetPortWindowPort(window);
  1148.     globalPt = topLeft(GetWindowPort(window)->portRect);
  1149.     LocalToGlobal(&globalPt);
  1150.     SetPort(oldPort);
  1151.     return globalPt;
  1152. }
  1153.  
  1154.  
  1155. /*******************************************************************************
  1156.  
  1157.     GetMainScreenRect
  1158.  
  1159.     Gets the bounding rectangle of the main screen (the one with the menu bar
  1160.     on it). This rectangle includes the area that contains the menubar. For
  1161.     example, on a Mac Classic, this routine should return (0, 0, 512, 342).
  1162.  
  1163. *******************************************************************************/
  1164. Rect    HsoiGetMainScreenRect(void)
  1165. {
  1166.     GDHandle    mainDevice;
  1167.     GrafPtr        mainPort;
  1168.  
  1169.     if (gHasColorQD) {
  1170.         mainDevice = GetMainDevice();
  1171.         return (*mainDevice)->gdRect;
  1172.     } else {
  1173.         GetWMgrPort(&mainPort);
  1174.         return mainPort->portRect;
  1175.     }
  1176. }
  1177.  
  1178.  
  1179.  
  1180.  
  1181. // this gets our UPP's initialzed...called at startup time
  1182.  
  1183. void    HsoiInitMenuWindowUPPs( void )
  1184. {
  1185.     gTileSetupUPP = NewHsoiSetUpProc( HsoiTileSetup );
  1186.     gTileTheWindowUPP = NewHsoiWindowActionProc( HsoiTileTheWindow );
  1187.     gStackSetupUPP = NewHsoiSetUpProc( HsoiStackSetup );
  1188.     gStackTheWindowUPP = NewHsoiWindowActionProc( HsoiStackTheWindow );
  1189.     gLookForSelectedWindowUPP = NewHsoiWindowActionProc( HsoiLookForSelectedWindow );
  1190.     gCountWindowsUPP = NewHsoiWindowActionProc( HsoiCountWindows );
  1191.     gLookForPreviousWindowUPP = NewHsoiWindowActionProc( HsoiLookForPreviousWindow );
  1192.     gCountSomeWindowUPP = NewHsoiWindowActionProc( HsoiCountSomeWindow );
  1193.  
  1194.     return;
  1195. }
  1196.  
  1197. // do just what the function name says...readjust the window.  when the window itself
  1198. // gets resized and moved by the stacking/tiling, we'll have to readjust the
  1199. // "internal" window stuff, like our text area and scrollbars, etc.
  1200.  
  1201. void    HsoiReadjustAfterStackTile( WindowRef window )
  1202. {
  1203.     Rect    r;
  1204.     
  1205.     // the biggie that actually readjusts everything
  1206.     
  1207.     HsoiViewChanged( window );
  1208.     
  1209.     // refigure out our text rect
  1210.     
  1211.     HsoiCalcTextRect( window, &r );
  1212.     
  1213.     // and then "invalidate" that text rect so the Window Manager issues an
  1214.     // update event to redraw that rect's contents
  1215.      
  1216.     InvalRect( &r );
  1217.  
  1218.     return;
  1219. }
  1220.  
  1221.  
  1222. // the following 2 functions are utility functions to allow Knaster/Rollin's technique
  1223. // of maintaining the window list by creating a "linked list" of the windows using
  1224. // the window's refCon field.  but since HAS already uses the window's refCon (to store
  1225. // a reference to the window's associated DocumentRecord), we can't use Knaster/Rollin's
  1226. // technique as is.  So instead, we've got our own "refCon" (of sorts) in the DocumentRecord
  1227. // (called nextWindow) where we'll store this next window's address.  In essence, these
  1228. // functions act the same as GetWRefCon/SetWRefCon (but get/set from the DocumentRecord).
  1229. // if you want to extract this tiling/stacking code from HAS and you won't be using the
  1230. // window's refCon for anything, you could just use the window's refCon to store the
  1231. // linked list data and then throughout this code, just replace HsoiGetNextWindow with
  1232. // GetWRefCon and HsoiSetNextWindow with SetWRefCon.
  1233.  
  1234. long    HsoiGetNextWindow( WindowRef window )
  1235. {
  1236.     // we should check to ensure this is a document window (i.e. a window with a
  1237.     // DocumentRecord stored in the refCon), but for now, we'll just assume this
  1238.     // is the case
  1239.  
  1240.     return (*(DocumentHandle)GetWRefCon( window ))->nextWindow;
  1241. }
  1242.  
  1243. void    HsoiSetNextWindow( WindowRef window, long data )
  1244. {
  1245.     // we should check to ensure this is a document window (i.e. a window with a
  1246.     // DocumentRecord stored in the refCon), but for now, we'll just assume this
  1247.     // is the case
  1248.     
  1249.     (*(DocumentHandle)GetWRefCon( window ))->nextWindow = data;
  1250.     
  1251.     return;
  1252. }
  1253.